unit oMatrixCreating;

interface

uses uOSPList, Types, oCellularWorld, oCell, StdCtrls, oEntity, oPathsManager, Generics.Collections, oCoordinates, oPhysicalEntity,
     SysUtils, oPathFinding, Dialogs;

type

//toto zas pouzivam v CreateMatrix
tCalcCell2 = class(tNode)
  private
    aX : integer;//position in tCellularWorld - Miso 25.5
    aY : integer;//position in tCellularWorld - Miso 25.5
    aReference : tCalcCell2;
    aValue : integer;
    aDirection : tPoint;
  public
    property Direction  : tPoint read aDirection  write aDirection ;
    property X : integer read aX write aX;
    property Y : integer read aY write aY;
    constructor Create(paCell : tCell);
end;

//toto je vypocet matice vyhodnosti
procedure SetMatrixes(paWorld : tCellularWorld; paPath: TPathsManager.TPathType);
function CreateMatrix( paLevel : tCellularWorld; paStart, paGoal : tCell) : fMatrix;
function GetNeighbourCells(paLevel : tCellularWorld; paMatrix : fMatrix; paCell : tCalcCell2; paNotIn1, paNotIn2 : tOspList) : tOspList; overload;
function IsIn(paCell : tCell; paList : tOspList) : boolean;
procedure Insert(paWhat, paTo : tOspList);
function GetShift(paFrom, paTo : tCalcCell2) : tPoint;
function VypocitajHodnotu(paCell : tCalcCell2) : integer;
function VypocitajHodnotu2(paCell : tCalcCell2) : integer;
procedure VypisZoznam(paZoznam : tOspList; paMemo : tMemo);
function getCentreOfCell(paX, paY: integer): tCoordinates;
procedure OtocSvet(paWorld : tCellularWorld);

var     Matica : fMatrix;

implementation

uses oFormDocasneVykreslovanieNaGrid;

procedure OtocSvet(paWorld : tCellularWorld);
  var i, j, sirka, vyska : integer;
begin
  sirka := length(paWorld.Cells) - 1;
  vyska := length(paWorld.Cells[0]) - 1;
  for i := 0 to sirka do
    for j := 0 to vyska do
      paWorld.Cell[i,j].Y := vyska - paWorld.Cell[i,j].Y;
end;



procedure SetMatrixes(paWorld : tCellularWorld ; paPath: TPathsManager.TPathType);
  var start, goal : tEntity;
      i : integer;
      StartPos, GoalPos : tPoint;
      WayPoints : Tlist<TEntity>;
      Matrixes : TList<fMatrix>;
begin
  //OtocSvet(paWorld);
  WayPoints := paPath.WayPoints;
  Matrixes := paPath.WayMatrixes;
  for i := 0 to WayPoints.Count - 2 do begin
    Start := WayPoints.Items[i];
    Goal := WayPoints.Items[i + 1];
    paWorld.detectCell(Start.Position, StartPos);
    paWorld.detectCell(Goal.Position, GoalPos);
    //Docasny.UkazWorld(paWorld);
    //ShowMessage(inttostr(paWorld.Cells[StartPos.X, StartPos.y].X) + ' , ' + inttostr(paWorld.Cells[StartPos.X, StartPos.y].Y));
    Matrixes.Add(CreateMatrix(paWorld, paWorld.Cells[StartPos.X, StartPos.y], paWorld.Cells[GoalPos.X, GoalPos.y]));
  end;
  //OtocSvet(paWorld);
end;


function getCentreOfCell(paX, paY: integer): tCoordinates;
var
  p : tCoordinates;
  rect : cRect;
begin
  rect.TopLeft := TCoordinates.Create;
  rect.BottomRight := TCoordinates.Create;
  rect.TopLeft.y := (paY + 1) * 10;
  rect.BottomRight.y := paY * 10;
  rect.TopLeft.x := paX * 10;
  rect.BottomRight.x := (paX + 1) * 10;
  p := tCoordinates.Create(paX, paY);
  p.x := (rect.TopLeft.x + rect.BottomRight.x)/2;
  p.y := (rect.TopLeft.y + rect.BottomRight.y)/2;
  rect.TopLeft.Free;
  rect.BottomRight.Free;
  Result := p;
end;

procedure VypisZoznam(paZoznam : tOspList; paMemo : tMemo);
  var pom : tCalcCell2;
      s : string;
begin
  pom := tCalcCell2(paZoznam.GetFirst);
  s := '';
  while pom <> nil do begin
    if pom.aReference = nil then
      s := s + inttostr(pom.aX) + ',' + inttostr(pom.aY) + '; ' + 'nil,nil' + '; ' + inttostr(pom.aValue) + ' | '
    else
      s := s + inttostr(pom.aX) + ',' + inttostr(pom.aY) + '; ' + inttostr(pom.aReference.X) + ',' + inttostr(pom.aReference.Y) +'; ' + inttostr(pom.aValue) + ' | ';
    pom := tCalcCell2(pom.GetSucc) ;
  end;
  paMemo.Lines.Add(s);
  paMemo.Lines.Add('------------------------');
end;

function VypocitajHodnotu2(paCell : tCalcCell2) : integer;
  var res : integer;
      Shift : tPoint;
begin
  Shift := GetShift(paCell.aReference, paCell);
  if Abs(Shift.x) = Abs(Shift.y) then begin
    if (abs(paCell.Direction.X - Shift.x) > 1) or (abs(paCell.Direction.Y - Shift.Y) > 1)  then
      res := 1
    else
      res := -1;
  end
  else begin
    if (abs(paCell.Direction.X - Shift.x) > 1) or (abs(paCell.Direction.Y - Shift.Y) > 1)  then
      res := 2
    else
      res := 1;
  end;
  result := paCell.aReference.aValue + res;
end;

function VypocitajHodnotu(paCell : tCalcCell2) : integer;
  var x, y, res,pom1,pom2, Value : integer;
begin
  res := 1;
  result := paCell.aReference.aValue + res;
end;



function CreateMatrix(paLevel : tCellularWorld; paStart, paGoal : tCell) : fMatrix;
  var Mat : fMAtrix;
      i,j : integer;
      actual, pom : tCalcCell2;
      Value : integer;
      Vnutorny, Vonkajsi, Susedia, Way : tOspList;
      Pocitaj : boolean;
      Direction : tPoint;
      s : string;
begin
  //nastavenie pola
  setlength(Matica, paLEvel.Columns);
  for i := 0 to length(Matica) - 1 do
    setlength(Matica[i], paLevel.Rows);
  //inicializacia hodnot na - MaxInt
  for I := 0 to length(Matica) - 1 do
    for j := 0 to Length(Matica[i]) - 1 do
      if paLevel.Cell[i, j].IsBarrier then Matica[i][j] := MaxInt
      else Matica[i][j] := - MaxInt;
  //vytvorim zoznamy
  Vonkajsi := tOspList.Create;
  Vnutorny := tOspList.Create;
  // ziskanie cesty
  Way := getWay(paLEvel, paStart, paGoal);
  if paStart.Way = 0 then paStart.Way := Hodnota
  else paStart.Way := 3;
  //vlozim aj start do cesty
  Way.FirstIn(paStart);
  //vytvorim z cesty vnutorny zoznam
  while NOT Way.Empty do begin
    actual := tCalcCell2.Create(tCell(Way.FirstOut));
    Vnutorny.LastIn(actual);
  end;
  // hodnota pre startovaciu bunku
  Value := 0;
  //najprv ocislujem cestu a vytvorim prvy vonkajsi zoznam
  actual := tCalcCell2(Vnutorny.GetLast);
  while actual <> nil do begin
    actual.aReference := nil;
    actual.aValue := Value;
    Matica[actual.x][actual.y] := Value;
    actual.Direction := GetShift(actual, tCalcCell2(actual.GetPred));
    if abs(actual.Direction.x) = abs(actual.Direction.y)  then // ak idem po uhlopriecke, pripocitam 2
      Value := Value + 2
    else
      Value := Value + 1;                                            // inak 1
    Susedia := GetNeighbourCells(paLevel, Matica, actual, Vnutorny, Vonkajsi);// najdem susedov co nie su vo vnutornom a ani vo vonkajsom zozname
    s := '';
    Insert(Susedia, Vonkajsi);
    actual := tCalcCell2(actual.GetPred);
  end;
 // Docasny.StringGrid2.Repaint;
  //prehodim zoznamy
  {Vnutorny.Clear;
  FreeAndNil(Vnutorny);  }
  Vnutorny := Vonkajsi;
  Vonkajsi := tOspList.Create;
  // tuna je cyklus co to cele ocisluje
  Pocitaj := true;
  while Pocitaj do begin
    actual := tCalcCell2(Vnutorny.GetLast);
    while actual <> nil do begin
//      actual.aValue := VypocitajHodnotu(actual);
      actual.aValue := VypocitajHodnotu2(actual);
      Matica[actual.x][actual.y] := actual.aValue;
      //Docasny.StringGrid2.Repaint;
      Susedia := GetNeighbourCells(paLevel, Matica, actual, Vnutorny, Vonkajsi);
      Insert(Susedia, Vonkajsi);
      actual := tCalcCell2(actual.GetPred);
    end;
   // Docasny.StringGrid2.Repaint;
    if Vonkajsi.Empty then begin
      Pocitaj := False;
      //uvolnim zoznamy
      Vnutorny.Clear;
      Vonkajsi.Clear;
      FreeAndNil(Vnutorny);
      FreeAndNil(Vonkajsi);
    end
    else begin
      //prehodim zoznamy
      {Vnutorny.Clear;
      FreeAndNil(Vnutorny); }
      Vnutorny := Vonkajsi;
      Vonkajsi := tOspList.Create;
    end;
  end;
  result := Matica;
end;


function GetNeighbourCells(paLevel : tCellularWorld; paMAtrix : fMatrix; paCell: tCalcCell2; paNotIn1, paNotIn2 : tOspList) : tOspList;
  var i, j : integer;
      list : tOspList;
      pom : tCalcCell2;
begin
  list := tOspList.Create;
  for i := -1 to 1 do
    for j := -1 to 1 do begin
      if IsIndexOK(paLevel, paCell.X + i, paCell.Y + j) then // ak sa nepytam mimo
      if Not ((i = 0) and (j = 0)) and (abs(i) <> abs(j)) then //ak to nie je bunka na susedov ktorej sa pytam
      if Not paLevel.Cell[paCell.X + i, paCell.Y + j].IsBarrier then // ak to nie je bariera
      if Not IsIn(paLevel.Cell[paCell.X + i, paCell.Y + j], paNotIn1) then // ak to nie je sucastou NotIn1 listu
      if Not IsIn(paLevel.Cell[paCell.X + i, paCell.Y + j], paNotIn2) then
      if paMatrix[paCell.X + i, paCell.Y + j] = - MaxInt then begin //ak sa hodnota este nepocitala
        pom := tCalcCell2.Create(paLevel.Cell[paCell.X + i, paCell.Y + j]);
        pom.aReference := paCell; // nastavim referencnu bunku
        pom.aDirection := paCell.Direction;
        list.FirstIn(pom);
      end;
    end;
  result := list;
end;

function GetShift(paFrom, paTo : tCalcCell2) : tPoint;
  var point : tPoint;
begin
  if paTo <> nil then begin
    point.X := paTo.X - paFrom.X;
    point.Y := paTo.Y - paFrom.Y;
  end
  else begin
    point.X := 0;
    point.Y := 0;
  end;
  Result := point;
end;

procedure Insert(paWhat, paTo : tOspList);
  var pom : tCalcCell2;
begin
  while NOT paWhat.Empty  do begin
    pom := tCalcCell2(paWhat.FirstOut);
    paTo.LastIn(pom);
  end;
  FreeAndNil(paWhat);
end;

function IsIn(paCell : tCell; paList : tOspList) : boolean;
  var res : boolean;
      pom : tCalcCell2;
begin
  res := false;
  pom := tCalcCell2(paList.GetFirst);
  while (pom <> nil) and (res = false) do begin
    if (pom.X = paCell.X) and (pom.Y = paCell.Y) then begin
      res := true;
    end;
    pom := tCalcCell2(pom.GetSucc);
  end;
  result := res;
end;

constructor tCalcCell2.Create(paCell: TCell);
begin
  inherited Create;
  aX := paCell.X;
  aY := paCell.Y;
  aReference := nil;
end;

end.
